package web

import (
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/golang-jwt/jwt/v5"
	uuid "github.com/lithammer/shortuuid/v4"
	"go.uber.org/zap"
	"jk-time/webook/internal/service/oauth2"
	"jk-time/webook/internal/service/oauth2/wechat"
	ijwt "jk-time/webook/internal/web/jwt"
	"net/http"
	"time"
)

type OAuth2WechatHandler struct {
	svc oauth2.Service
	ijwt.RedisJWTHandler
	StateKey string
}

func NewOAuth2WechatHandler(svc *wechat.WeChatService) *OAuth2WechatHandler {
	return &OAuth2WechatHandler{
		svc:      svc,
		StateKey: "k6CswdUm77WKcbM68UxquxVsHSpTCwgK",
	}
}

func (h *OAuth2WechatHandler) RegisterRoutes(server *gin.Engine) {
	g := server.Group("/oauth2/wechat")
	g.GET("/authurl", h.AuthURL)
	g.POST("/callback", h.Callback)
}

func (h *OAuth2WechatHandler) AuthURL(ctx *gin.Context) {
	state := uuid.New()
	url, err := h.svc.AuthURL(ctx, state)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "构造扫码登录失败",
		})
		return
	}

	err = h.setTokenCookie(ctx, state, err)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("setTokenCookie失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Data: url,
	})
	return
}

// 当生成 AuthURL 的时候，我们标识一下这一次的会话，将 state 和这一次的请求绑定在一起。等到回调回来的时候，
// 我们看看回调中的 state 是不是我们生成的时候用的 state.
func (h *OAuth2WechatHandler) setTokenCookie(ctx *gin.Context, state string, err error) error {
	//校验state, state始终一致
	stateToken := jwt.NewWithClaims(jwt.SigningMethodHS512, &StateClaims{
		State: state,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 3)),
		},
	})
	stateTokenStr, err := stateToken.SignedString([]byte(h.StateKey))
	if err != nil {
		return fmt.Errorf("token签名失败,%w", err)
	}
	ctx.SetCookie("jwt-state", stateTokenStr, 600, "/oauth2/wechat/callback",
		"", false, true)
	return nil
}
func (h *OAuth2WechatHandler) Callback(ctx *gin.Context) {
	err := h.verifyState(ctx)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("校验失败:", zap.Error(err))
		return
	}
	code := ctx.Query("code")
	// 临时授权码，去获取真正的授权码
	info, err := h.svc.VerifyCode(ctx, code)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("获取真正授权码失败:", zap.Error(err))
		return
	}
	u, err := h.svc.FindOrCreateByInfo(ctx, info)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("创建或生成用户信息失败:", zap.Error(err))
		return
	}

	err = h.SetLoginToken(ctx, u.Id)
	if err != nil {
		ctx.JSON(http.StatusOK, Result{
			Code: 5,
			Msg:  "系统错误",
		})
		zap.L().Error("生成登录token失败:", zap.Error(err))
		return
	}
	ctx.JSON(http.StatusOK, Result{
		Msg: "OK",
	})
}

func (h *OAuth2WechatHandler) verifyState(ctx *gin.Context) error {
	state := ctx.Query("state")
	//校验一下我的state
	ck, err := ctx.Cookie("jwt-state")
	if err != nil {
		return fmt.Errorf("拿不到原始的error,%w", err)
	}

	var stateClaims StateClaims
	stateToken, err := jwt.ParseWithClaims(ck, &stateClaims, func(token *jwt.Token) (interface{}, error) {
		return h.StateKey, nil
	})
	if err != nil || !stateToken.Valid {
		return fmt.Errorf("token 已经过期,%w", err)
	}
	if stateClaims.State != state {
		return errors.New("state 不相等")
	}
	return nil
}

type StateClaims struct {
	State string
	jwt.RegisteredClaims
}
